#ifndef WII_GI_C_LIST_H
#define WII_GI_C_LIST_H

#include "e_ErrorCodes.h"
#include <SFML/System.hpp>

namespace sfx
{
   /**
    * List element base class
    */
   class c_ListElem
   {
      public:
   
         /**
          * constructor
          */
         c_ListElem()
         {
            reset();
            
            return;
         }
         
         /**
          * destructor
          */
         virtual ~c_ListElem()
         {
            return;
         }
   
         c_ListElem *getObjOwner() const
         {
            return( m_ObjOwner );
         }
   
         c_ListElem *setObjOwner(c_ListElem *ObjOwner)
         {
            c_ListElem *PreviousOwner = m_ObjOwner;
   
            m_ObjOwner = ObjOwner;
   
            return(PreviousOwner);
         }
   
         c_ListElem *getPrevObj(void)
         {
            return(m_PrevObj);
         }
   
         c_ListElem *getNextObj(void)
         {
            return(m_NextObj);
         }
   
         e_ErrorCode insertObj(c_ListElem *PrevObj, c_ListElem *NextObj);
         e_ErrorCode insertObjBefore(c_ListElem *InsertObj);
         e_ErrorCode insertObjAfter(c_ListElem *InsertObj);
         c_ListElem* removeObjsBetween(c_ListElem *EndObj);
         c_ListElem* removeObj(void);
   
         c_ListElem* getLastObj(void);
         c_ListElem* getFirstObj(void);
         e_ErrorCode   joinList(c_ListElem * Obj);
         unsigned getNumObjInList(void);
   
         void reset(void)
         {
            m_PrevObj = 0;
            m_NextObj = 0;
            m_ObjOwner = 0;
         }
   
      private:
         c_ListElem *m_ObjOwner;
         c_ListElem *m_PrevObj;
         c_ListElem *m_NextObj;
   
         void setPrevObj(c_ListElem *PrevObj)
         {
            m_PrevObj = PrevObj;
         }
   
         void setNextObj(c_ListElem *NextObj)
         {
            m_NextObj = NextObj;
         };
   
   };   
   
   
   //List that contains c_ListElements
   template <class T>
   class c_List : public c_ListElem
   {
      public:
         c_List(sf::Mutex* Mutex = 0 );
   
         virtual ~c_List();
   
         void setMutex(sf::Mutex* Mutex)
         {
            m_Mutex = Mutex;
         }
   
         e_ErrorCode addHead(T *AddObj);
         e_ErrorCode addTail(T *AddObj);
   
         e_ErrorCode insertBefore(T *ObjToInsertBefore, T *ObjToInsert);
         e_ErrorCode insertAfter(T *ObjToInsertAfter, T *ObjToInsert);
   
         T *removeHead();
         T *removeTail();
   
         T* getHead()
         {
            return m_ObjHead;
         }
   
         T* getTail()
         {
            return m_ObjTail;
         }
   
         T* getNext(T *Obj)
         {
            dbgAssert(Obj != 0);
            dbgAssert(Obj->getObjOwner() == this);
   
            return (T *) Obj->getNextObj();
         }
   
         T* getPrev(T *Obj)
         {
            dbgAssert(Obj != 0);
            dbgAssert(Obj->getObjOwner() == this);
   
            return (T *) Obj->getPrevObj();
         }
   
         e_ErrorCode removeObject( T* Obj );
   
         e_ErrorCode removeAll(void);
   
         unsigned getListSize(void)
         {
            return(m_ListSize);
         }
   
         bool checkIfInList(T *Obj)
         {
            if(Obj && (Obj->getObjOwner() == this))
            {
               return(true);
            }
            else
            {
               return(false);
            }
         }
         
      private:
         e_ErrorCode lockList() const
         {
            if( m_Mutex )
            {
               m_Mutex->Lock();
            }
            return d_Success;
         }
   
         e_ErrorCode unlockList() const
         {
            if( m_Mutex )
            {
               m_Mutex->Unlock();
            }
            return d_Success;
         }
         
      private:
         sf::Mutex* m_Mutex;
         T*         m_ObjHead;
         T*         m_ObjTail;
         unsigned   m_ListSize;
   };
   
   template <class T>
   c_List<T>::c_List(sf::Mutex* Mutex/* = 0*/)
   {
      m_ObjHead = 0;
      m_ObjTail = 0;
   
      m_ListSize = 0;
   
      m_Mutex = Mutex;
   }
   
   template <class T>
   c_List<T>::~c_List()
   {
      assert(m_ObjHead == 0);
      assert(m_ObjTail == 0);
   }
   
   template <class T>
   e_ErrorCode c_List<T>::addHead(T *AddObj)
   {
      assert( AddObj != 0 );
      assert( AddObj->getObjOwner() == 0 );
      
      e_ErrorCode Status( d_Unkown );
   
      Status = lockList();
      if( Status == d_Success )
      {
         if (m_ObjHead == 0)
         {
            m_ObjHead = m_ObjTail = AddObj;
         }
         else
         {
            m_ObjHead->insertObjBefore(AddObj);
            m_ObjHead = AddObj;
         }
      
         m_ListSize++;
         AddObj->setObjOwner(this);
      
         unlockList();
      }
      
      assert( Status != d_Unkown );
      return(Status);
   }
   
   template <class T>
   e_ErrorCode c_List<T>::addTail(T *AddObj)
   {
      dbgAssert(AddObj != 0);
      dbgAssert(AddObj->getObjOwner() == 0);
      
      e_ErrorCode Status( d_Unkown );
   
      Status = lockList();
      if( Status == d_Success )
      {
         if (m_ObjTail == 0)
         {
            m_ObjHead = m_ObjTail = AddObj;
         }
         else
         {
            m_ObjTail->insertObjAfter(AddObj);
            m_ObjTail = AddObj;
         }
      
         m_ListSize++;
         AddObj->setObjOwner(this);
      
         unlockList();
      }
      
      assert( Status != d_Unkown );
      return(Status);
   }
   
   template <class T>
   e_ErrorCode c_List<T>::insertBefore(T *ObjToInsertBefore, T *ObjToInsert)
   {
      assert(ObjToInsertBefore != 0);
      assert(ObjToInsert != 0);
      assert(ObjToInsertBefore->getObjOwner() == this);
      assert(ObjToInsert->getObjOwner() == 0);
      
      e_ErrorCode Status(d_Unkown);
      
      Status = lockList();
      if( Status == d_Success )
      {
         ObjToInsertBefore->insertObjBefore(ObjToInsert);
         if (m_ObjHead == ObjToInsertBefore)
         {
            m_ObjHead = ObjToInsert;
         }
      
         m_ListSize++;
         ObjToInsert->setObjOwner(this);
      
         unlockList();
      }
      
      assert( Status != d_Unkown );
      return( Status );
   }
   
   template <class T>
   e_ErrorCode c_List<T>::insertAfter(T *ObjToInsertAfter, T *ObjToInsert)
   {
      assert(ObjToInsertAfter != 0);
      assert(ObjToInsert != 0);
      assert(ObjToInsertAfter->getObjOwner() == this);
      assert(ObjToInsert->getObjOwner() == 0);
      
      e_ErrorCode Status(d_Unkown);
   
      Status = lockList();
      if( Status == d_Success )
      {
         ObjToInsertAfter->insertObjAfter(ObjToInsert);
      
         if (m_ObjTail == ObjToInsertAfter)
         {
            m_ObjTail = ObjToInsert;
         }
      
         m_ListSize++;
         ObjToInsert->setObjOwner(this);
      
         unlockList();
      }
      
      assert( Status != d_Unkown );
      return( Status );
   }
   
   template <class T>
   T* c_List<T>::removeHead()
   {
      T* Obj = 0;

      if( d_Success  == lockList() )
      { 
         Obj = getHead();
         if( Obj )
         {
            assert(Obj->getObjOwner() == this);
            removeObject(Obj);
         }
      
         unlockList();
      }
      
      return( Obj );
   }
   
   template <class T>
   T *c_List<T>::removeTail()
   {
      T* Obj( 0 );
      
      if( d_Success  == lockList() )
      {
         Obj = getTail();
         if( Obj )
         {
            assert( Obj->getObjOwner() == this );
      
            removeObject(Obj);
         }
      
         unlockList();
      }
   
      return( Obj );
   }
   
   template <class T>
   e_ErrorCode c_List<T>::removeObject(T *Obj )
   {
      assert(Obj != 0);
      assert(Obj->getObjOwner() == this);
      
      e_ErrorCode Status( d_Unkown );
   
      Status = lockList();
      if( Status == d_Success )
      {
   
         if (m_ObjHead == Obj)
         {
            m_ObjHead = (T *) Obj->getNextObj();
         }
      
         if (m_ObjTail == Obj)
         {
            m_ObjTail = (T *) Obj->getPrevObj();
         }
      
         Obj->setObjOwner( 0 );
      
         m_ListSize--;
      
         unlockList();
      }
      
      assert( d_Unkown != Status );
      return( Status );
   }
   
   template <class T>
   e_ErrorCode c_List<T>::removeAll()
   {
      e_ErrorCode Status( d_Unkown );
   
      Status = lockList();
      if( Status == d_Success )
      {
         T* Obj;
         T* NextObj;
   
         Obj = getHead();
         while (Obj)
         {
            dbgAssert(Obj->getObjOwner() == this);
      
            removeObject(Obj, & NextObj);
            Obj = NextObj;
         }
      
         unlockList();
      }
      
      assert( d_Unkown != Status );
      return( Status );
   }
   
}

#endif //WII_GI_C_LIST_H


